package com.katze.boot.applet.asn1.controller;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.katze.boot.applet.asn1.adapter.*;
import com.katze.boot.applet.asn1.anlysis.AnalysisHelper;
import com.katze.boot.applet.asn1.enity.TransAsn;
import com.katze.boot.file.DirectoryEnum;
import com.katze.boot.file.entity.FileTransfer;
import com.katze.boot.file.registry.FileController;
import com.katze.boot.file.registry.FileSystemRegistrationBean;
import com.katze.boot.plugins.security.annotations.RestControllerMapping;
import com.katze.boot.plugins.security.annotations.RestRequestMapping;
import com.katze.common.CommonUtils;
import com.katze.common.DatePattern;
import com.katze.common.Message;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@RestControllerMapping("/asn1")
public class Asn1Controller extends FileController {
    private static final Logger log = LoggerFactory.getLogger(Asn1Controller.class);

    private static final String DECODE_DIR_SUFFIX = "_Decode";

    private final Resource resource = AnalysisHelper.getResource();

    private final ObjectMapper objectMapper = new ObjectMapper(){
        private final DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter("    ", DefaultIndenter.SYS_LF);
        @Override
        public SerializationConfig getSerializationConfig() {
            SerializationConfig config = super.getSerializationConfig();
            ((DefaultPrettyPrinter) config.getDefaultPrettyPrinter()).indentArraysWith(indenter);
            ((DefaultPrettyPrinter) config.getDefaultPrettyPrinter()).indentObjectsWith(indenter);
            return config;
        }
    }.enable(SerializationFeature.INDENT_OUTPUT);

    public Asn1Controller(FileSystemRegistrationBean fileSystemRegistrationBean) {
        super(fileSystemRegistrationBean, DirectoryEnum.ASN1);
    }

    @RestRequestMapping(value = "/view", description = "加载数据文件")
    public Message view(){
        if (resource.exists()) {
            try (InputStream stream = resource.getInputStream()) {
                List<TransAsn> list = objectMapper.readValue(stream, new TypeReference<>() {});
                return Message.ok().data(list);
            } catch (Exception e) {
                log.error("数据文件{}加载失败", resource, e);
            }
        }
        return Message.error("加载数据失败, 请查看数据文件: " + resource + "是否存在");
    }

    @RestRequestMapping(value = "/upload/config", method = RequestMethod.POST, description = "上传配置文件")
    public Message uploadConfig(MultipartFile file, HttpServletRequest request) {
        String name = request.getParameter("name");
        String filename = file.getOriginalFilename();
        Path configDir;
        try {
            configDir = resource.getFile().toPath().resolveSibling("config");
            if (!Files.exists(configDir)) Files.createDirectories(configDir);
        } catch (IOException e) {
            throw new IllegalStateException("配置目录加载失败");
        }
        try {
            assert filename != null: "文件名获取失败";
            Files.write(configDir.resolve(filename), file.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
            this.update(name, filename, true);
            return Message.ok();
        } catch (Exception e){
            log.error("{}/{}文件写入失败", configDir, filename, e);
            throw new IllegalStateException("文件写入失败: " + e.getMessage());
        }
    }

    @RestRequestMapping(value = "/update", description = "编辑数据文件")
    public Message update(String name, String asn, boolean update){
        Path file;
        try {
            file = resource.getFile().toPath();
        } catch (Exception e){
            log.error("数据文件{}读取失败", resource, e);
            return Message.error("数据文件读取失败:" + e.getMessage());
        }
        synchronized(this) {
            try (InputStream stream = Files.newInputStream(file)){
                List<TransAsn> list = objectMapper.readValue(stream, new TypeReference<List<TransAsn>>() {});
                for (TransAsn transAsn : list) {
                    if (transAsn.getName().equals(name)){
                        if (update){
                            transAsn.getAsn().add(asn);
                        } else {
                            transAsn.getAsn().remove(asn);
                        }
                        break;
                    }
                }
                Files.write(file, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(list), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
                return Message.ok(asn + (update? "添加成功":"删除成功"));
            } catch (Exception e){
                log.error("数据文件{}写入失败", resource, e);
                return Message.error("数据文件写入失败:" + e.getMessage());
            }
        }
    }

    /**
     * 处理已上传文件
     * @param transfer
     */
    @Override
    public void datafile(FileTransfer transfer) {
        Path dir = dataDir.resolve(transfer.getOwner()).resolve(transfer.getP1()).resolve(DatePattern.localDateTime(DatePattern.PURE_DATE_PATTERN));
        try {
            if (!Files.exists(dir)) {
                Files.createDirectories(dir);
            }
            Files.copy(transfer.getTemp().toPath(), dir.resolve(transfer.getName()), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            log.error("{}创建失败", dir);
            throw new IllegalStateException(e.getMessage());
        }
    }

    @RestRequestMapping(value = "/data", skip = true)
    public Message getData(String user, String asn) throws IOException {
        Path dir = dataDir.resolve(user).resolve(asn);
        Map<String, List<Object[]>> result = new LinkedHashMap<>();
        if (Files.exists(dir)) {
            try (Stream<Path> walk = Files.walk(dir, 1)) {
                walk.filter(item -> !item.equals(dir) && Files.isDirectory(item))
                        .map(Path::toFile)
                        .sorted(Comparator.comparing(o -> Long.parseLong(o.getName()), Comparator.reverseOrder()))
                        .forEach(o -> {
                            List<Object[]> names = Stream.of(Objects.requireNonNull(o.listFiles())).filter(File::isFile)
                                    .sorted(Comparator.comparing(File::lastModified))
                                    .map(f -> {
                                        String[] items = new File(f.getAbsolutePath() + DECODE_DIR_SUFFIX).list((folder, name) -> name.startsWith("info_"));
                                        String status = items == null || items.length == 0? "ready": items[0].split("_")[2];
                                        return new Object[]{f.getName(), f.getAbsolutePath(), status};
                                    })
                                    .collect(Collectors.toList());
                            if (!names.isEmpty()) {
                                result.put("d" + o.getName(), names);
                            }
                        });
            }
        }
        return Message.ok().data(result);
    }

    @RestRequestMapping(value = "/analysis", description = "解析数据文件")
    public Message analysis(String user, String asn, String path){
        if (resource.exists()) {
            List<TransAsn> transAsns = new ArrayList<>();
            try (InputStream stream = resource.getInputStream()) {
                transAsns = objectMapper.readValue(stream, new TypeReference<List<TransAsn>>() {});
            } catch (Exception e) {
                e.printStackTrace();
            }
            Path file = Paths.get(path);
            Path output = file.resolveSibling(file.getFileName() + "_Decode");
            for (TransAsn transAsn : transAsns) {
                if (transAsn.getAsn() != null && transAsn.getAsn().contains(asn)){
                    BasicAdapter adapter;
                    switch (transAsn.getType()){
                        case "BinaryCommonAdapter":
                            adapter = BinaryCommonAdapter.getInstance(file, asn, transAsn.getLength(), output);
                            break;
                        case "ASN1ContainHeadAdapter":
                            adapter = ASN1ContainHeadAdapter.getInstance(file, asn, transAsn.getLength(), output);
                            break;
                        case "ASN1FixedLengthAdapter":
                            adapter = ASN1FixedLengthAdapter.getInstance(file, asn, transAsn.getLength(), output);
                            break;
                        case "ASN1CommonAdapter":
                            adapter = ASN1CommonAdapter.getInstance(file, asn, output);
                            break;
                        case "ASN1GroupAdapter":
                            adapter = ASN1GroupAdapter.getInstance(file, asn, output);
                            break;
                        default: throw new NullPointerException("未匹配解析类型");
                    }
                    Thread thread = new Thread(adapter::run);
                    thread.start();
                    break;
                }
            }
        }
        return Message.ok("正在解析中,稍后查看");
    }

    @RestRequestMapping(value = "/search", description = "读取解析结果")
    public Message search(String keyword, String path, int start){
        Path dir = Paths.get(path + DECODE_DIR_SUFFIX);
        String[] value = null;
        if (Files.exists(dir)) {
            try {
                String[] info = dir.toFile().list((folder, name) -> name.startsWith("info_"));
                if (info != null) {
                    value = info[0].split("_");
                } else {
                    value = new String[]{"info","0","error"};
                }
            } catch (Exception e){
                e.printStackTrace();
                return Message.error("读取解析结果文件失败");
            }
        }
        if (value == null) {
            return Message.ok("数据文件尚未解析");
        }
        int size = 1000;
        if ("decode".equals(value[2])){
            return Message.ok("正在解析...");
        } else if ("success".equals(value[2])){
            try (LineIterator it = FileUtils.lineIterator(dir.resolve("data.txt").toFile())) {
                List<Map<String, Object>> data = new LinkedList<>();
                int total;
                int index = 0;
                int count = start;
                if (keyword != null && !keyword.isEmpty()){
                    while (it.hasNext()){
                        count ++;
                        String line = it.nextLine();
                        if (line.contains(keyword)){
                            Map<String, Object> item = new HashMap<>();
                            item.put("no", count);
                            item.put("content", line);
                            data.add(item);
                        }
                    }
                    total = data.size();
                } else {
                    while (it.hasNext()){
                        if (index == start){
                            count ++;
                            Map<String, Object> item = new HashMap<>();
                            item.put("no", count);
                            item.put("content", it.nextLine());
                            data.add(item);
                            if (data.size() == size) break;
                        } else {
                            it.nextLine();
                            index ++;
                        }
                    }
                    total = Integer.parseInt(value[1]);
                    index = count;
                }
                Map<String, Object> rs = new HashMap<>();
                rs.put("data", data);
                rs.put("total", total);
                rs.put("start", index);
                return Message.ok().data(rs);
            } catch (IOException e){
                e.printStackTrace();
            }
        }
        return Message.error("解析失败,请检查数据文件与配置文件相对应");
    }

    @RestRequestMapping(value = "/remove", method = RequestMethod.DELETE, description = "删除数据文件")
    public Message remove(String[] files){
        for (String file : files) {
            File item = new File(file);
            try {
                CommonUtils.deleteDir(Paths.get(file + DECODE_DIR_SUFFIX));
                if (item.delete()){
                    if (Objects.requireNonNull(item.getParentFile().list()).length == 0){
                        item.getParentFile().delete();
                    }
                }
            } catch (Exception e) {
                log.error("{}删除失败", file);
                return Message.error(item.getName() + "删除失败");
            }
        }
        return Message.ok("删除成功");
    }
}
